18-3 讀 ASCII 仵

fgetl 函數可將 ASCII 檔案的內容中的某一列讀出,並將該列的 ASCII 內容以轉成字串傳回。例如,下列指令可將檔案 mean.m 逐列印出:

Example 1: 18-檔案讀寫/fgetl01.mfid = fopen('mean.m', 'r'); while feof(fid)==0 % feof 測試檔案指標是否已到達結束位置 line = fgetl(fid); disp(line); endfunction y = mean(x,dim,flag,flag2) %MEAN Average or mean value. % S = MEAN(X) is the mean value of the elements in X if X is a vector. % For matrices, S is a row vector containing the mean value of each % column. % For N-D arrays, S is the mean value of the elements along the first % array dimension whose size does not equal 1. % % MEAN(X,DIM) takes the mean along the dimension DIM of X. % % S = MEAN(..., TYPE) specifies the type in which the mean is performed, % and the type of S. Available options are: % % 'double' - S has class double for any input X % 'native' - S has the same class as X % 'default' - If X is floating point, that is double or single, % S has the same class as X. If X is not floating point, % S has class double. % % S = MEAN(..., MISSING) specifies how NaN (Not-A-Number) values are % treated. The default is 'includenan': % % 'includenan' - the mean of a vector containing NaN values is also NaN. % 'omitnan' - the mean of a vector containing NaN values is the mean % of all its non-NaN elements. If all elements are NaN, % the result is NaN. % % Example: If X = [1 2 3; 3 3 6; 4 6 8; 4 7 7]; % % then mean(X,1) is [3 4.5 6] and mean(X,2) is [2; 4; 6; 6] % % Class support for input X: % float: double, single % integer: uint8, int8, uint16, int16, uint32, % int32, uint64, int64 % % See also MEDIAN, STD, MIN, MAX, VAR, COV, MODE. % Copyright 1984-2014 The MathWorks, Inc. isDimSet = nargin > 1 && ~ischar(dim); isFlag2Set = nargin >= 4; if nargin == 1 || (nargin == 2 && isDimSet) flag = 'default'; omitnan = false; else % nargin >= 3 || (nargin == 2 && ~isDimSet) if nargin == 2 flag = dim; elseif nargin == 3 if ~isDimSet flag2 = dim; isFlag2Set = true; end elseif nargin == 4 && ~isDimSet error(message('MATLAB:mean:nonNumericSecondInput')); end if ~isFlag2Set flag2 = ''; end [flag, omitnan] = parseInputs(flag, flag2, isFlag2Set); end if ~isDimSet % preserve backward compatibility with 0x0 empty if isequal(x,[]) y = sum(x,flag)/0; return end dim = find(size(x)~=1,1); if isempty(dim), dim = 1; end end if ~isobject(x) && isinteger(x) isnative = (lower(flag(1)) == 'n'); if intmin(class(x)) == 0 % unsigned integers y = sum(x,dim,flag); if (isnative && all(y(:) < intmax(class(x)))) || ... (~isnative && all(y(:) <= flintmax)) % no precision lost, can use the sum result y = y/size(x,dim); else % throw away and recompute y = intmean(x,dim,isnative); end else % signed integers ypos = sum(max(x,0),dim,flag); yneg = sum(min(x,0),dim,flag); if (isnative && all(ypos(:) < intmax(class(x))) && ... all(yneg(:) > intmin(class(x)))) || ... (~isnative && all(ypos(:) <= flintmax) && ... all(yneg(:) >= -flintmax)) % no precision lost, can use the sum result y = (ypos+yneg)/size(x,dim); else % throw away and recompute y = intmean(x,dim,isnative); end end else if omitnan % Compute sum and number of NaNs m = sum(x, dim, flag, 'omitnan'); nr_nonnan = size(x, dim) - matlab.internal.math.countnan(x, dim); % Divide by the number of non-NaNs. y = m ./ nr_nonnan; else y = sum(x, dim, flag)/size(x,dim); end end end function y = intmean(x, dim, isnative) % compute the mean of integer vector shift = [dim:ndims(x),1:dim-1]; x = permute(x,shift); xclass = class(x); if ~isnative outclass = 'double'; else outclass = xclass; end if intmin(xclass) == 0 accumclass = 'uint64'; else accumclass = 'int64'; end xsiz = size(x); xlen = cast(xsiz(1),accumclass); y = zeros([1 xsiz(2:end)],outclass); ncolumns = prod(xsiz(2:end)); int64input = isa(x,'uint64') || isa(x,'int64'); for iter = 1:ncolumns xcol = cast(x(:,iter),accumclass); if int64input xr = rem(xcol,xlen); ya = sum((xcol-xr)./xlen,1,'native'); xcol = xr; else ya = zeros(accumclass); end xcs = cumsum(xcol); ind = find(xcs == intmax(accumclass) | (xcs == intmin(accumclass) & (xcs < 0)) , 1); while (~isempty(ind)) remain = rem(xcs(ind-1),xlen); ya = ya + (xcs(ind-1) - remain)./xlen; xcol = [remain; xcol(ind:end)]; xcs = cumsum(xcol); ind = find(xcs == intmax(accumclass) | (xcs == intmin(accumclass) & (xcs < 0)), 1); end if ~isnative remain = rem(xcs(end),xlen); ya = ya + (xcs(end) - remain)./xlen; % The latter two conversions to double never lose precision as % values are less than FLINTMAX. The first conversion may lose % precision. y(iter) = double(ya) + double(remain)./double(xlen); else y(iter) = cast(ya + xcs(end) ./ xlen, outclass); end end y = ipermute(y,shift); end function [flag, omitnan] = parseInputs(flag, flag2, isFlag2Set) % Process flags, return boolean omitnan and string flag if ~isrow(flag) error(message('MATLAB:mean:invalidFlags')); end s = strncmpi(flag, {'omitnan', 'includenan'}, max(length(flag), 1)); if ~isFlag2Set omitnan = s(1); if any(s) flag = 'default'; end else if ~isrow(flag2) error(message('MATLAB:mean:invalidFlags')); end s2 = strncmpi(flag2, {'omitnan', 'includenan'}, max(length(flag2), 1)); % Make sure one flag is from the set {'omitnan', 'includenan'}, % while the other is from {'default', 'double', 'native'}. if ~xor( any(s), any(s2) ) error(message('MATLAB:mean:invalidFlags')); end if any(s) % flag contains 'includenan' or 'omitnan' omitnan = s(1); flag = flag2; else omitnan = s2(1); end end end

執行上述程式後,MATLAB 會先在目前目錄找尋 mean.m,若找不到,再根據搜尋路徑,找出 mean.m 指令的位置,然後再將其內容一列一列地列出。(為節省空間,在此不列印出結果,請讀者自行試用此範例程式碼。)若要知道此 mean.m 所在的位置,可輸入「which mean」即可顯示此檔案所在的路徑。

fget 函數和 fgetl 函數很相似,兩者均可由檔案讀取一列資料,其差別在於:fgetl 會捨去換行字元,而 fgets 函數則保留換行字元。

利用 fgetl 函數,我們可以模擬在 UNIX 系統下的 grep 指令,來找出包含某一特定字串的一列,其 MATLAB 函數 grep.m 可撰寫如下:

>> type grep.m function grep(filename, pattern) fid = fopen(filename, 'r'); line_number = 0; while feof(fid) == 0, line = fgetl(fid); matched = findstr(line, pattern); if ~isempty (matched) fprintf('%d: %s \n', line_number,line); end line_number = line_number + 1; end fclose(fid);

例如,欲列出 grep.m 中包含 'matched' 字串的每一列,可輸入如下:

>> grep('grep.m', 'matched')<xmp> <xmp class=code> 6: matched = findstr(line, pattern); 7: if ~isempty (matched)

Hint
若要進行更複雜的字串比對,例如要在一個檔案中找出「b 和 d 中間夾一至三個母音」的英文字,請參考本書姊妹作「MATLAB 程式設計:進階篇」中的第四章「通用運算式」。

若已知 ASCII 檔案的格式,欲進行更精確的讀取,可用 fscanf 函數來從檔案中讀取格式化之資料,其使用語法如下:

matrix = fscanf(fid, format)

其中 fid 是欲讀取之檔案的辨識碼(由 fopen 產生),format 是格式指定字串(Format Specifier),用以指定讀入資料的型態,常用的格式指定字串有下列幾種:

其他各種格式指定字串可輸入 help fscanf 來得到詳細的線上說明。例如,有一 ASCII 檔案 test.txt 的內容如下:

>> type test.txt 1 4 9 16 25 36 49 64 81 100

欲使用 fscanf 指令讀取其內容,可輸入如下:

Example 2: 18-檔案讀寫/fscanf01.mfid = fopen('test.txt', 'r'); myData = fscanf(fid, '%g'); fclose(fid); myData % 顯示 myData myData = 1 4 9 16 25 36 49 64 81 100

上例也顯示了 MATLAB 的 fscanf 指令和 C 的 fscanf 指令的最大不同:MATLAB 的 fscanf 指令是向量化的(Vectorized),只要讀入資料的型態正確,MATLAB 的 fscanf 指令會一再執行,並把所得結果存放於一個向量並回傳。

若要限制傳回向量的大小,或是希望傳回一矩陣,則可在 fscanf 加上第三個輸入引數,以指定傳回向量(或矩陣)的大小。例如:欲讀取 test.txt 的前 3 筆資料,可輸入如下:

Example 3: 18-檔案讀寫/fscanf02.mfid = fopen('test.txt', 'r'); myData = fscanf(fid, '%g', 3); fclose(fid); myData % 顯示 myData myData = 1 4 9

欲使 fscanf 指令傳回一個 3×2 的矩陣,可輸入如下:

Example 4: 18-檔案讀寫/fscanf03.mfid = fopen('test.txt', 'r'); myData = fscanf(fid, '%g', [3, 2]); fclose(fid); myData % 顯示 myData myData = 1 16 4 25 9 36

Hint
fscanf 在讀取檔案時,是一列一列進行的。但在傳回矩陣時,是將資料一行一行地填入欲傳回的矩陣。(在本書中,「列」即指「橫列」,「行」即指「直行」。)

sscanf 函數和 fscanf 的功能很類似,唯一不同的是,sscanf 函數從字串(Strings)中讀取資料,而不是從檔案(Files)。例如:

Example 5: 18-檔案讀寫/sscanf01.mstr = num2str([pi, sqrt(2), log10(3)]) retrieved = sscanf(str, '%g') str = 3.1416 1.4142 0.47712 retrieved = 3.1416 1.4142 0.4771


MATLAB程式設計:入門篇